`timescale 1ns / 1ps
`include "../include/cpu_defines.sv"
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2023/06/11 15:04:22
// Design Name: 
// Module Name: BranchPredictor
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////

module BranchPredictor #(
	parameter COUNTER_SIZE=3,
	parameter USE_SIZE=2
)(
    input logic cpu_clk,
	input logic cpu_rst_n,
	input logic clear,
	input logic front_clear,
	input logic btb_clear,
	input logic stall,

	input logic [31: 0] pc,
	input logic [31: 0] pc_next,
	input logic request_success,
	output logic btb_en,
	output logic btb_delay,
	output logic [31: 0] btb_addr,

	input BranchInfo inst_branch_info_i,
	input logic store_en,
	input logic jalr,
	input logic [1: 0] analyze_result,
	input logic [31: 0] analyze_addr,
	input logic [31: 0] current_addr,
	input logic ras_re,
	input logic ras_we,
	input logic [1: 0] ras_up,
	input logic [1: 0] ras_down,

	input logic dis_branch_en,
	input logic dis_j_en,
	input logic [4: 0] dis_rob_index,
	input logic [31: 0] dis_analyze_addr,
	input logic wb_branch_en1,
	input logic wb_branch_en2,
	input logic [`BRANCH_BUFFER_WIDTH-1: 0] wb_branch_index1,
	input logic [`BRANCH_BUFFER_WIDTH-1: 0] wb_branch_index2,
	input logic [4: 0] wb_rob_index1,
	input logic [4: 0] wb_rob_index2,
	input logic [31: 0] wb_branch1,
	input logic [31: 0] wb_branch2,
	input logic retire_en,
	input logic retire_branch,
	input logic [5: 0] retire_rob_index,
	input logic branch_fail,

	output logic [`BRANCH_BUFFER_WIDTH-1: 0] branch_index,
	output logic branch_error1,
	output logic branch_error2,

	output logic [31: 0] branch_addr,
	output logic [31: 0] branch_fail_addr,
	output BranchInfo inst_branch_info_o,
	output logic full,
	output logic predict
    );

    typedef struct{
		logic _type;
		logic jalr;
		logic predict;
		logic provider_branch;
		logic alt_branch;
		logic real_branch;
		logic new_entry;
		logic table_hit;
		logic [`PREDICTOR_BASE_INDEX_SIZE-1: 0] base_index;
		logic [`TAGE_TABLE_SIZE-1: 0][`PREDICTOR_INDEX_SIZE-1: 0] index;
		logic [`TAGE_TABLE_SIZE-1: 0][`PREDICTOR_TAG_SIZE-1: 0] tag;
		logic [`PREDICT_TABLE_SIZE-1: 0] provider, alt_provider;
		logic [31: 0] predict_addr;
		logic [31: 0] normal_addr;
		logic [31: 0] real_addr;
    } inst_info;

	localparam [8: 0] table_origin_length[`TAGE_TABLE_SIZE-1: 0] = {9'd4, 9'd6, 9'd11, 9'd17, 9'd28, 9'd45, 9'd73, 9'd118, 9'd191, 9'd309};
	// localparam [8: 0] table_origin_length[`TAGE_TABLE_SIZE-1: 0] = {9'd4, 9'd6, 9'd11, 9'd17};
    inst_info inst_buffer[`BRANCH_BUFFER_SIZE-1: 0];
	logic [31: 0][31: 0] inst_real_addr;
	logic [`TAGE_TABLE_SIZE-1: 0][`PREDICTOR_INDEX_SIZE-1: 0] compress_index, compress_index_o, r_compress_index, r_compress_index_o;
	logic [`TAGE_TABLE_SIZE-1: 0][11: 0] compress_tag1, compress_tag1_o, r_compress_tag1, r_compress_tag1_o;
	logic [`TAGE_TABLE_SIZE-1: 0][9: 0] compress_tag2, compress_tag2_o, r_compress_tag2, r_compress_tag2_o;
	logic table_hit;
	logic [`TAGE_TABLE_SIZE-1: 0] tag_hits_priority, alt_tag_hits_priority;
	logic [`BRANCH_BUFFER_SIZE-1: 0] buffer_en;
	logic [`BRANCH_BUFFER_SIZE-1: 0] buffer_index_ready;
	logic [`BRANCH_BUFFER_WIDTH-1: 0] buffer_head, buffer_tail, rob_tail;
	logic [`BRANCH_BUFFER_WIDTH: 0] remain_count;

	logic [4: 0] alt_ctr;
	logic [`TAGE_TABLE_SIZE: 0] predictions;
	logic prediction, alt_predict_branch;
	logic update_en;
	logic update_use;
	logic [`PREDICT_TABLE_SIZE-1: 0] providers, alt_providers;
	logic [`PREDICTOR_INDEX_SIZE-1: 0] hash_indexs[`TAGE_TABLE_SIZE-1: 0];
	logic [`PREDICTOR_TAG_SIZE-1: 0] hash_tags[`TAGE_TABLE_SIZE-1: 0];
	logic [`TAGE_TABLE_SIZE-1: 0][`PREDICTOR_INDEX_SIZE-1: 0] hashIndexs_ghist, hashIndexs_ghist_i;
	logic [`TAGE_TABLE_SIZE-1: 0][`PREDICTOR_TAG_SIZE-1: 0] hashTags_ghist, hashTags_ghist_i;
	logic [`TAGE_TABLE_SIZE-1: 0] tag_hits, alt_tag_hits;
	logic [`TAGE_TABLE_SIZE-1: 0] dec_uses;
	logic [`TAGE_TABLE_SIZE-1: 0] u;
	logic [`TAGE_TABLE_SIZE-1: 0] allocs, allocs_before;
	logic [`TAGE_TABLE_SIZE-1: 0] new_entrys;
	logic [31: 0] normal_addr;

	BranchInfo branch_info_direct, branch_info_save;
	logic request_success_next;
	logic [31: 0] pc_stage2;
	logic [`TAGE_TABLE_SIZE-1: 0] tag_hits_next;
	logic [`PREDICTOR_INDEX_SIZE-1: 0] hash_indexs_next[`TAGE_TABLE_SIZE-1: 0];
	logic [`PREDICTOR_TAG_SIZE-1: 0] hash_tags_next[`TAGE_TABLE_SIZE-1: 0];

	logic [`PREDICT_TABLE_SIZE-1: 0] update_provider, alloc_provider;
	logic [`TAGE_TABLE_SIZE: 0][`PREDICTOR_INDEX_SIZE-1: 0] update_index;
	logic [`PREDICTOR_BASE_INDEX_SIZE-1: 0] update_base_index;
	logic [`TAGE_TABLE_SIZE-1: 0][`PREDICTOR_TAG_SIZE-1: 0] update_tag;
	logic update_real_branch;

	logic [`PREDICTOR_INDEX_SIZE-1: 0] pcFold;
	logic [`PREDICTOR_TAG_SIZE-1: 0] pcFoldTag;
	logic [310: 0] ghist, retire_ghist, next_retire_ghist;
	logic [31: 0] path_hist, retire_path_hist, next_retire_path_hist;
	logic [7: 0] path, retire_path;
	logic [31: 0] retire_pc;
	logic [5: 0] branch_rob_index;
	logic branch_fail_next; 
// stage 1
	assign hashIndexs_ghist = hashIndexs_ghist_i;
	assign hashTags_ghist = hashTags_ghist_i;
	GetTag #(
		.COMPRESS1_LENGTH(12),
		.COMPRESS2_LENGTH(10),
		.INDEX_SIZE(`PREDICTOR_INDEX_SIZE),
		.TAG_SIZE(`PREDICTOR_TAG_SIZE)
	) get_tag0(
		.pc(pc),
		.path_hist(path_hist),
		.compress_index1(0),
		.compress_index2(compress_index[0]),
		.compress1(compress_tag1[0]),
		.compress2(compress_tag2[0]),
		.tag(hashTags_ghist_i[0])
	);
	for(genvar i=1; i<`TAGE_TABLE_SIZE; i++)begin
		GetTag #(
			.COMPRESS1_LENGTH(12),
			.COMPRESS2_LENGTH(10),
			.INDEX_SIZE(`PREDICTOR_INDEX_SIZE),
			.TAG_SIZE(`PREDICTOR_TAG_SIZE)
		) get_tag(
			.pc(pc),
			.path_hist(path_hist),
			.compress_index1(compress_index[i-1]),
			.compress_index2(compress_index[i]),
			.compress1(compress_tag1[i]),
			.compress2(compress_tag2[i]),
			.tag(hashTags_ghist_i[i])
		);
	end

	for(genvar i=0; i<`TAGE_TABLE_SIZE; i++)begin
		GetIndex #(
			.COMPRESS_LENGTH(`PREDICTOR_INDEX_SIZE),
			.INDEX_SIZE(`PREDICTOR_INDEX_SIZE),
			.BANK(i)
		)get_index(
			.pc(pc),
			.path_hist(path_hist),
			.compress(compress_index[i]),
			.index(hashIndexs_ghist_i[i])
		);
	end

	// assign pcFold = pc[6:0]^pc[13:7]^pc[20:14]^pc[27:21];
	// assign pcFold = pc[7:0]^pc[15:8]^pc[23:16]^pc[31:24];
	assign pcFold = pc[31:22]^pc[21:12]^pc[11:2];
	assign pcFoldTag = pc[8: 0] ^ pc[17: 9] ^ pc[26: 18] ^ {4'b0, pc[31: 27]};

	localparam T0 = `PREDICT_TABLE_SIZE'h1;
    localparam T1 = `PREDICT_TABLE_SIZE'h2;
    localparam T2 = `PREDICT_TABLE_SIZE'h4;
    localparam T3 = `PREDICT_TABLE_SIZE'h8;
    localparam T4 = `PREDICT_TABLE_SIZE'h10;
	localparam T5 = `PREDICT_TABLE_SIZE'h20;
	localparam T6 = `PREDICT_TABLE_SIZE'h40;
	localparam T7 = `PREDICT_TABLE_SIZE'h80;
	localparam T8 = `PREDICT_TABLE_SIZE'h100;
	localparam T9 = `PREDICT_TABLE_SIZE'h200;
	localparam T10 = `PREDICT_TABLE_SIZE'h400;

	for(genvar i=0; i<`TAGE_TABLE_SIZE; i++)begin
		assign hash_indexs[i] = hashIndexs_ghist[i];
		assign hash_tags[i] = hashTags_ghist[i];
		TageTable #(
			.INDEX_SIZE(`PREDICTOR_INDEX_SIZE),
			.TAG_SIZE(`PREDICTOR_TAG_SIZE),
			.COUNTER_SIZE(COUNTER_SIZE),
			.USE_SIZE(USE_SIZE)
		) tage_table(
			.cpu_clk(cpu_clk),
			.cpu_rst_n(cpu_rst_n),
			.real_branch(update_real_branch),
			.update_en(update_en),
			.provider(update_provider[i]),
			.alloc(allocs[i]),
			.update_use(update_use),
			.dec_use(dec_uses[i]),
			.update_index(update_index[i]),
			.search_index(hash_indexs[i]),
			.search_tag(hash_tags_next[i]),
			.update_tag(update_tag[i]),
			.new_entry(new_entrys[i]),
			.prediction(predictions[i+1]),
			.tag_hit(tag_hits[i]),
			.out_u(u[i])
		);
	end

	BHT #(.INDEX_SIZE(`PREDICTOR_BASE_INDEX_SIZE), .READ_LATENCY(1)) bht(
		.cpu_clk(cpu_clk),
		.update_en(update_en && update_provider == T0),
		.real_branch(update_real_branch),
		.search_index(pc[`PREDICTOR_BASE_INDEX_SIZE-1: 0]^pc[`PREDICTOR_BASE_INDEX_SIZE+1: 2]),
		.update_index(update_base_index),
		.prediction(predictions[0])
	);

	logic btb_update_en, btb_update_delay, btb_en_next;
	logic [5: 0] btb_search_index, btb_update_index;
	logic [29: 0] btb_update_tag;
	logic [31: 0] btb_update_addr, btb_addr_next;
	assign btb_search_index = pc[9: 4] ^ pc[15: 10] ^ pc[21: 16] ^ pc[27: 22];
	assign btb_update_en = !clear && (analyze_result == 2'b10 || retire_en && inst_buffer[buffer_head].jalr);
	assign btb_update_tag = retire_en && inst_buffer[buffer_head].jalr ? retire_pc[31: 2] : current_addr[31: 2];
	assign btb_update_index = retire_en && inst_buffer[buffer_head].jalr ? retire_pc[9: 4] ^ retire_pc[15: 10] ^ retire_pc[21: 16] ^ retire_pc[27: 22] : current_addr[9: 4] ^ current_addr[15: 10] ^ current_addr[21: 16] ^ current_addr[27: 22];
	assign btb_update_delay = retire_en && inst_buffer[buffer_head].jalr ? retire_pc[2] : current_addr[2];
	assign btb_update_addr = retire_en && inst_buffer[buffer_head].jalr ? inst_buffer[buffer_head].real_addr : analyze_addr;
	// 提前执行j, jal, jalr指令，这三条指令的跳转地址相对固定
	Btb #(.INDEX_SIZE(6)) btb(
		.cpu_clk(cpu_clk),
		.cpu_rst_n(cpu_rst_n),
		.clear(btb_clear),
		.search_index(btb_search_index),
		.search_tag(pc[31: 2]),
		.update_en(btb_update_en),
		.update_delay(btb_update_delay),
		.update_index(btb_update_index),
		.update_tag(btb_update_tag),
		.update_addr(btb_update_addr),
		.out_en(btb_en),
		.out_delay(btb_delay),
		.out_addr(btb_addr)
	);

// stage 2

	always_ff @(posedge cpu_clk)begin
		pc_stage2 <= pc;
		tag_hits_next <= tag_hits;
		btb_addr_next <= btb_addr;
		btb_en_next <= btb_en;
		request_success_next <= request_success;
		for(int i=0; i<`TAGE_TABLE_SIZE; i++)begin
			hash_indexs_next[i] <= hash_indexs[i];
			hash_tags_next[i] <= hash_tags[i];
		end
		if(request_success_next)begin
			branch_info_save <= branch_info_direct;
		end
	end
	assign table_hit = |tag_hits;
	priority_selector_reverse_multi #(`TAGE_TABLE_SIZE) selector_tag_hits(tag_hits, tag_hits_priority);
	assign providers = table_hit ? tag_hits_priority << 1 : T0;
    assign prediction =  |(providers & predictions);
	assign alt_tag_hits = tag_hits_priority ^ tag_hits;
	priority_selector_reverse_multi #(`TAGE_TABLE_SIZE) selector_alt_tag_hits(alt_tag_hits, alt_tag_hits_priority);
	assign alt_providers = |alt_tag_hits ? alt_tag_hits_priority << 1 : T0;
	assign alt_predict_branch = |(alt_providers & predictions);

	assign inst_branch_info_o = request_success_next ? branch_info_direct : branch_info_save;
	assign branch_info_direct.providers = providers;
	assign branch_info_direct.alt_provider = alt_providers;
	assign branch_info_direct.table_hit = table_hit;
	assign branch_info_direct.new_entry = (new_entrys & tag_hits_priority);
	assign branch_info_direct.predict = prediction;
	assign branch_info_direct.alt_predict = alt_predict_branch;
	assign branch_info_direct.btb_addr = btb_addr_next;
	assign branch_info_direct.btb_en = btb_en_next;
	for(genvar gen=0; gen<`TAGE_TABLE_SIZE; gen++)begin
		assign branch_info_direct.tag[gen] = hash_tags_next[gen];
	end

	assign branch_info_direct.base_index = pc_stage2[`PREDICTOR_BASE_INDEX_SIZE-1: 0]^pc_stage2[`PREDICTOR_BASE_INDEX_SIZE+1: 2];
	for(genvar i=0; i<`TAGE_TABLE_SIZE; i++)begin
		assign branch_info_direct.index[i] = hash_indexs_next[i];
	end
// stage3
	logic [31: 0] ras_addr;
	RAS ras(
		.cpu_clk(cpu_clk),
		.cpu_rst_n(cpu_rst_n),
		.clear(clear),
		.ras_up1(ras_up[0]),
		.ras_up2(ras_up[1]),
		.ras_down1(ras_down[0]),
		.ras_down2(ras_down[1]),
		.we(ras_we),
		.re(ras_re),
		.addr(normal_addr),
		.out_addr(ras_addr)
	);

	assign normal_addr = current_addr + 8;
	assign full = remain_count == 0;
	assign branch_addr = inst_branch_info_i.btb_en && (analyze_result == 2'b11 && jalr || analyze_result == 2'b10) ? pc_next : analyze_result == 2'b11 ? ras_addr : analyze_addr;
	assign branch_index = rob_tail;
	always_comb begin
		if(inst_branch_info_i.new_entry && alt_ctr[3])begin
			predict = inst_branch_info_i.alt_predict;
		end
		else begin
			predict = inst_branch_info_i.predict;
		end
	end
	always_ff @(posedge cpu_clk)begin
		if(cpu_rst_n == 1'b0 || clear)begin
			for(int i=0; i<`BRANCH_BUFFER_SIZE; i++)begin
				inst_buffer[i]._type <= 0;
				inst_buffer[i].predict <= 0;
				inst_buffer[i].provider_branch <= 0;
				inst_buffer[i].alt_branch <= 0;
				inst_buffer[i].real_branch <= 0;
				inst_buffer[i].index <= 0;
				inst_buffer[i].tag <= 0;
				inst_buffer[i].provider <= 0;
				inst_buffer[i].normal_addr <= 0;
				inst_buffer[i].predict_addr <= 0;
				inst_buffer[i].new_entry <= 0;
				inst_buffer[i].alt_provider <= 0;
				inst_buffer[i].table_hit <= 0;
				inst_buffer[i].jalr <= 0;
				inst_buffer[i].base_index <= 0;
				inst_buffer[i].real_addr <= 0;
			end
			buffer_en <= 0;
			buffer_head <= 0;
			buffer_tail <= 0;
			rob_tail <= 0;
			remain_count <= `BRANCH_BUFFER_SIZE;
			buffer_index_ready <= 0;
			inst_real_addr <= 0;
		end
		else begin
			buffer_head <= buffer_head + retire_en;
			remain_count <= remain_count - (store_en) + retire_en;
			rob_tail <= rob_tail + dis_branch_en;

			buffer_index_ready <= (buffer_index_ready | 
			(`BRANCH_BUFFER_SIZE'b1 << rob_tail)) & 
			(~({`BRANCH_BUFFER_SIZE{store_en}} & (1 << buffer_tail)));
			buffer_en <= (buffer_en | ({`BRANCH_BUFFER_SIZE{store_en}} & (`BRANCH_BUFFER_SIZE'b1 << buffer_tail)))
						& (~({`BRANCH_BUFFER_SIZE{retire_en}} & (`BRANCH_BUFFER_SIZE'b1 << buffer_head)));
			if(store_en)begin
				inst_buffer[buffer_tail]._type <= analyze_result == 2'b11;
				inst_buffer[buffer_tail].predict <= predict;
				inst_buffer[buffer_tail].provider_branch <= inst_branch_info_i.predict;
				inst_buffer[buffer_tail].alt_branch <= inst_branch_info_i.alt_predict;
				inst_buffer[buffer_tail].index <= inst_branch_info_i.index;
				inst_buffer[buffer_tail].base_index <= inst_branch_info_i.base_index;
				inst_buffer[buffer_tail].tag <= inst_branch_info_i.tag;
				inst_buffer[buffer_tail].provider <= inst_branch_info_i.providers;
				inst_buffer[buffer_tail].predict_addr <= analyze_result == 2'b11 && jalr && inst_branch_info_i.btb_en ? inst_branch_info_i.btb_addr : branch_addr;
				inst_buffer[buffer_tail].normal_addr <= normal_addr;
				inst_buffer[buffer_tail].new_entry <= inst_branch_info_i.new_entry;
				inst_buffer[buffer_tail].alt_provider <= inst_branch_info_i.alt_provider;
				inst_buffer[buffer_tail].table_hit <= inst_branch_info_i.table_hit;
				inst_buffer[buffer_tail].jalr <= jalr;
				buffer_tail <= buffer_tail + 1;
			end

			if(dis_j_en)begin
				inst_real_addr[dis_rob_index] <= dis_analyze_addr;
			end

			if(wb_hit1)begin
				// inst_real_addr[wb_rob_index1] <= inst_buffer[wb_branch_index1]._type ? wb_branch1 : wb_branch1[0] ? inst_buffer[wb_branch_index1].predict_addr : inst_buffer[wb_branch_index1].normal_addr;
				inst_buffer[wb_branch_index1].real_addr <= wb_branch1;
				inst_buffer[wb_branch_index1].real_branch <= wb_branch1[0];
			end
			if(wb_hit2)begin
				// inst_real_addr[wb_rob_index2] <= inst_buffer[wb_branch_index2]._type ? wb_branch2 : wb_branch2[0] ? inst_buffer[wb_branch_index2].predict_addr : inst_buffer[wb_branch_index2].normal_addr;
				inst_buffer[wb_branch_index2].real_addr <= wb_branch2;
				inst_buffer[wb_branch_index2].real_branch <= wb_branch2[0];
			end
		end
	end

// update
	logic wb_hit1, wb_hit2;
	logic correct;

	assign wb_hit1 = wb_branch_en1 && buffer_en[wb_branch_index1] && buffer_index_ready[wb_branch_index1];
	assign wb_hit2 = wb_branch_en2 && buffer_en[wb_branch_index2] && buffer_index_ready[wb_branch_index2];
	assign branch_error1 = wb_hit1 && (inst_buffer[wb_branch_index1].predict != wb_branch1[0] && !inst_buffer[wb_branch_index1]._type || inst_buffer[wb_branch_index1]._type && inst_buffer[wb_branch_index1].predict_addr != wb_branch1);
	assign branch_error2 = wb_hit2 && (inst_buffer[wb_branch_index2].predict !=	 wb_branch2[0] && !inst_buffer[wb_branch_index2]._type || inst_buffer[wb_branch_index2]._type && inst_buffer[wb_branch_index2].predict_addr != wb_branch2);

	assign branch_rob_index = retire_rob_index - 1;
	assign branch_fail_addr = !retire_branch ? inst_real_addr[branch_rob_index[5: 1]] : inst_buffer[buffer_head]._type ? inst_buffer[buffer_head].real_addr : inst_buffer[buffer_head].real_branch ? inst_buffer[buffer_head].predict_addr : inst_buffer[buffer_head].normal_addr;

	assign update_index = inst_buffer[buffer_head].index;
	assign update_base_index = inst_buffer[buffer_head].base_index;
	always_ff @(posedge cpu_clk)begin
		update_en <= retire_en & ~inst_buffer[buffer_head]._type;
		correct <= inst_buffer[buffer_head].predict == inst_buffer[buffer_head].real_branch;
		update_tag <= inst_buffer[buffer_head].tag;
		update_real_branch <= inst_buffer[buffer_head].real_branch;
		update_use <= inst_buffer[buffer_head].provider_branch != inst_buffer[buffer_head].alt_branch;
		if(cpu_rst_n == 1'b0)begin
			update_provider <= 0;
			alloc_provider <= 0;
		end
		else begin
			alloc_provider <= ~((inst_buffer[buffer_head].provider << 1) - 1);
			update_provider <= inst_buffer[buffer_head].provider | inst_buffer[buffer_head].alt_provider;
		end
	end
	
	assign allocs_before = ~u & alloc_provider[`PREDICT_TABLE_SIZE-1: 1];
	assign allocs = allocs_before & ((~(allocs_before)) + 1);
	assign dec_uses = allocs == 0 ? 10'h3ff : alloc_provider & (allocs -1);

	assign next_retire_ghist = (retire_ghist << 1) | inst_buffer[buffer_head].real_branch;
	assign path = current_addr[7: 0] ^ current_addr[9: 2] ^ current_addr[11: 4];
	assign retire_pc = inst_buffer[buffer_head].normal_addr - 8;
	assign retire_path = retire_pc[7: 0] ^ retire_pc[9: 2] ^ retire_pc[11: 4];
	assign next_retire_path_hist = (retire_path_hist << 1) ^ retire_path;
	always_ff @(posedge cpu_clk) begin
		branch_fail_next <= branch_fail;
		if(cpu_rst_n == 1'b0)begin
			ghist <= 0;
			retire_ghist <= 0;
			alt_ctr <= 0;
			compress_index <= 0;
			compress_tag1 <= 0;
			compress_tag2 <= 0;
			r_compress_index <= 0;
			r_compress_tag1 <= 0;
			r_compress_tag2 <= 0;
			path_hist <= 0;
			retire_path_hist <= 0;
		end
		else begin
			if(inst_buffer[buffer_head].new_entry && (inst_buffer[buffer_head].provider_branch != inst_buffer[buffer_head].alt_branch) && inst_buffer[buffer_head].table_hit && !inst_buffer[buffer_head]._type)begin
				if ((inst_buffer[buffer_head].alt_branch == inst_buffer[buffer_head].real_branch) && (alt_ctr != 5'b11111))begin
					alt_ctr <= alt_ctr + 1;
				end
				else if ((inst_buffer[buffer_head].alt_branch != inst_buffer[buffer_head].real_branch) && alt_ctr != 5'b0)begin
					alt_ctr <= alt_ctr - 1;
				end
			end
			if((store_en & ~(&analyze_result)) | branch_fail)begin
				ghist <= branch_fail ? next_retire_ghist : (ghist << 1) | predict;
				path_hist <= branch_fail ? next_retire_path_hist : (path_hist << 1) ^ path;
				compress_index <= branch_fail ? r_compress_index_o : compress_index_o;
				compress_tag1 <= branch_fail ? r_compress_tag1_o : compress_tag1_o;
				compress_tag2 <= branch_fail ? r_compress_tag2_o : compress_tag2_o;
			end
			if(retire_en && !inst_buffer[buffer_head]._type)begin
				retire_ghist <= next_retire_ghist;
				retire_path_hist <= next_retire_path_hist;
				r_compress_index <= r_compress_index_o;
				r_compress_tag1 <= r_compress_tag1_o;
				r_compress_tag2 <= r_compress_tag2_o;
			end
		end
	end

	for(genvar i=0; i<`TAGE_TABLE_SIZE; i++)begin
		CompressHistory  #(
			.COMPRESS_LENGTH(`PREDICTOR_INDEX_SIZE),
			.ORIGIN_LENGTH(table_origin_length[i])
		)fcompress_index(
			.origin(compress_index[i]),
			.dir(predict),
			.reverse_dir(ghist[table_origin_length[i]-1]),
			.compress_hist(compress_index_o[i])
		);

		CompressHistory  #(
			.COMPRESS_LENGTH(12),
			.ORIGIN_LENGTH(table_origin_length[i])
		)fcompress_tag1(
			.origin(compress_tag1[i]),
			.dir(predict),
			.reverse_dir(ghist[table_origin_length[i]-1]),
			.compress_hist(compress_tag1_o[i])
		);

		CompressHistory  #(
			.COMPRESS_LENGTH(10),
			.ORIGIN_LENGTH(table_origin_length[i])
		)fcompress_tag2(
			.origin(compress_tag2[i]),
			.dir(predict),
			.reverse_dir(ghist[table_origin_length[i]-1]),
			.compress_hist(compress_tag2_o[i])
		);

		CompressHistory  #(
			.COMPRESS_LENGTH(`PREDICTOR_INDEX_SIZE),
			.ORIGIN_LENGTH(table_origin_length[i])
		)fr_compress_index(
			.origin(r_compress_index[i]),
			.dir(inst_buffer[buffer_head].real_branch),
			.reverse_dir(ghist[table_origin_length[i]-1]),
			.compress_hist(r_compress_index_o[i])
		);

		CompressHistory  #(
			.COMPRESS_LENGTH(12),
			.ORIGIN_LENGTH(table_origin_length[i])
		)fr_compress_tag1(
			.origin(r_compress_tag1[i]),
			.dir(inst_buffer[buffer_head].real_branch),
			.reverse_dir(retire_ghist[table_origin_length[i]-1]),
			.compress_hist(r_compress_tag1_o[i])
		);

		CompressHistory  #(
			.COMPRESS_LENGTH(10),
			.ORIGIN_LENGTH(table_origin_length[i])
		)fr_compress_tag2(
			.origin(r_compress_tag2[i]),
			.dir(inst_buffer[buffer_head].real_branch),
			.reverse_dir(retire_ghist[table_origin_length[i]-1]),
			.compress_hist(r_compress_tag2_o[i])
		);
	end
endmodule

module CompressHistory #(
	parameter COMPRESS_LENGTH=8,
	parameter ORIGIN_LENGTH=10,
	parameter OUTPUT_POINT=ORIGIN_LENGTH%COMPRESS_LENGTH
)(
	input logic [COMPRESS_LENGTH-1: 0] origin,
	input logic dir,
	input logic reverse_dir,
	output logic [COMPRESS_LENGTH-1: 0] compress_hist
);
	assign compress_hist = (origin << 1) ^ dir ^ (reverse_dir << OUTPUT_POINT) ^ (origin >> COMPRESS_LENGTH);
endmodule

module F #(
	parameter INDEX_SIZE=7
)(
	input logic [31: 0] path_hist,
	output logic [INDEX_SIZE-1: 0] out
);
	assign out = path_hist ^ (path_hist >> INDEX_SIZE);
endmodule

module GetIndex #(
	parameter COMPRESS_LENGTH=8,
	parameter INDEX_SIZE=7,
	parameter BANK=0,
	parameter SHIFT_SIZE=INDEX_SIZE > BANK ? INDEX_SIZE-BANK+1 : BANK-INDEX_SIZE+1
)(
	input logic [31: 0] pc,
	input logic [31: 0] path_hist,
	input logic [COMPRESS_LENGTH-1: 0] compress,
	output logic [INDEX_SIZE-1: 0] index
);
	logic [INDEX_SIZE-1: 0] func_out;
	F #(INDEX_SIZE)func(path_hist, func_out);
	assign index = pc ^ (pc >> SHIFT_SIZE) ^ compress ^ func_out;
endmodule

module GetTag #(
	parameter COMPRESS1_LENGTH=8,
	parameter COMPRESS2_LENGTH=8,
	parameter INDEX_SIZE=8,
	parameter TAG_SIZE=7
)(
	input logic [31: 0] pc,
	input logic [31: 0] path_hist,
	input logic [INDEX_SIZE-1: 0] compress_index1,
	input logic [INDEX_SIZE-1: 0] compress_index2,
	input logic [COMPRESS1_LENGTH-1: 0] compress1,
	input logic [COMPRESS2_LENGTH-1: 0] compress2,
	output logic [TAG_SIZE-1: 0] tag
);
	logic [COMPRESS1_LENGTH-1: 0] compress_index, tag_before, tag_expand;
	logic [TAG_SIZE-1: 0] func_out;
	F #(TAG_SIZE)func(path_hist, func_out);
	assign compress_index = (compress_index1 << 2) ^ pc ^ (pc >> 2) ^ compress_index2;
	assign tag_before = (compress_index >> 1) ^ (compress_index[0] << 10) ^ func_out ^ (compress1 ^ (compress2 << 1));
	assign tag_expand = tag_before ^ (tag_before >> TAG_SIZE);
	assign tag = tag_expand[TAG_SIZE-1: 0];
endmodule